home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume14 / pcomm / part06 < prev    next >
Encoding:
Internet Message Format  |  1988-05-18  |  43.5 KB

  1. Subject:  v14i104:  Dial out and terminal emulator, Part06/06
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: fthood!egray
  7. Posting-number: Volume 14, Issue 104
  8. Archive-name: pcomm/part06
  9.  
  10. #! /bin/sh
  11. # This is a shell archive, meaning:
  12. # 1. Remove everything above the #! /bin/sh line.
  13. # 2. Save the resulting text in a file.
  14. # 3. Execute the file with /bin/sh (not csh) to create:
  15. #    x_batch.c
  16. #    x_menu.c
  17. #    x_rcv.c
  18. #    x_send.c
  19. #    x_win.c
  20. #    xmodem.c
  21. export PATH; PATH=/bin:/usr/bin:$PATH
  22. echo shar: "extracting 'x_batch.c'" '(7780 characters)'
  23. if test -f 'x_batch.c'
  24. then
  25.     echo shar: "will not over-write existing file 'x_batch.c'"
  26. else
  27. sed 's/^X//' << \SHAR_EOF > 'x_batch.c'
  28. X/*
  29. X * Routines to support the batch protocols.
  30. X */
  31. X
  32. X#include <stdio.h>
  33. X#include <ctype.h>
  34. X#include <curses.h>
  35. X#include "misc.h"
  36. X#include "xmodem.h"
  37. X
  38. X/*
  39. X * Send the file name for the modem7 batch.  Only uses 11 characters
  40. X * of the filename.
  41. X */
  42. X
  43. Xint
  44. Xsend_modem7(win, name)
  45. XWINDOW *win;
  46. Xchar *name;
  47. X{
  48. X    char *new_name, *fix_name();
  49. X    unsigned char sum, calc_sum();
  50. X
  51. X                    /* convert to 11 character name */
  52. X    new_name = fix_name(name);
  53. X    sum = calc_sum((unsigned char *) new_name, 12);
  54. X
  55. X    putc_line(ACK);
  56. X                    /* for each character in the name */
  57. X    while (*new_name != CTRLZ) {
  58. X        putc_line((unsigned char) *new_name);
  59. X
  60. X        switch (getc_line(3)) {
  61. X            case ACK:    /* got it! */
  62. X                break;
  63. X            case CAN:    /* cancel transmission */
  64. X                if (getc_line(2) == CAN) {
  65. X                    beep();
  66. X                    clear_line(win, 12, 24, 1);
  67. X                    wattrstr(win, A_BOLD, "REMOTE ABORTED");
  68. X                    wrefresh(win);
  69. X                    return(CANCEL);
  70. X                }
  71. X                /* fall thru... */
  72. X            default:
  73. X                clear_line(win, 12, 24, 1);
  74. X                waddstr(win, "NAME FAILED");
  75. X                wrefresh(win);
  76. X                return(ERROR);
  77. X        }
  78. X        new_name++;
  79. X    }
  80. X    putc_line(CTRLZ);
  81. X                    /* verify the checksum */
  82. X    if (getc_line(10) != sum) {
  83. X        putc_line('u');
  84. X        clear_line(win, 12, 24, 1);
  85. X        waddstr(win, "CHECKSUM FAILED");
  86. X        wrefresh(win);
  87. X        return(ERROR);
  88. X    }
  89. X    putc_line(ACK);
  90. X    return(0);
  91. X}
  92. X
  93. X/*
  94. X * Receive a modem 7 file name.  A return code of 1 means the end of the
  95. X * batch transfers.
  96. X */
  97. X
  98. Xint
  99. Xrcv_modem7(win, default_err)
  100. XWINDOW *win;
  101. Xint default_err;
  102. X{
  103. X    int i, c, err_method, err_count, got_it;
  104. X    unsigned char sum, calc_sum();
  105. X    char temp_name[13];
  106. X    extern char *null_ptr, file_name[15];
  107. X    void change_name(), unfix_name();
  108. X                    /* send the first char */
  109. X    err_method = default_err;
  110. X    err_count = 0;
  111. X    got_it = 0;
  112. X    while (err_count < MAX_ERRORS) {
  113. X                    /* switch to checksum? */
  114. X        if (default_err == 1 && err_count > MAX_ERRORS/2)
  115. X            err_method = 0;
  116. X
  117. X        if (err_method)
  118. X            putc_line('C');
  119. X        else
  120. X            putc_line(NAK);
  121. X                    /* what'd we get? */
  122. X        if (getc_line(10) == ACK) {
  123. X            got_it++;
  124. X            break;
  125. X        }
  126. X        err_count++;
  127. X    }
  128. X    if (!got_it)
  129. X        return(ERROR);
  130. X                    /* get the name */
  131. X    for (i=0; i<12; i++) {
  132. X        c = getc_line(3);
  133. X
  134. X        switch(c) {
  135. X            case EOT:    /* end of batch? */
  136. X                return(-1);
  137. X            case CAN:    /* cancel transmission */
  138. X                if (getc_line(2) == CAN) {
  139. X                    beep();
  140. X                    clear_line(win, 12, 24, 1);
  141. X                    wattrstr(win, A_BOLD, "REMOTE ABORTED");
  142. X                    wrefresh(win);
  143. X                    return(CANCEL);
  144. X                }
  145. X                /* fall thru... */
  146. X            case 'u':    /* bad name character */
  147. X                beep();
  148. X                clear_line(win, 12, 24, 1);
  149. X                wattrstr(win, A_BOLD, "BAD NAME");
  150. X                wrefresh(win);
  151. X                return(ERROR);
  152. X            default:    /* the name... */
  153. X                temp_name[i] = c;
  154. X                if (c != CTRLZ)
  155. X                    putc_line(ACK);
  156. X                break;
  157. X        }
  158. X    }
  159. X    temp_name[12] = NULL;
  160. X                    /* send our checksum */
  161. X    sum = calc_sum((unsigned char *) temp_name, 12);
  162. X    putc_line(sum);
  163. X                    /* do they agree ? */
  164. X    if (getc_line(10) != ACK) {
  165. X        beep();
  166. X        clear_line(win, 12, 24, 1);
  167. X        wattrstr(win, A_BOLD, "BAD NAME");
  168. X        wrefresh(win);
  169. X        return(ERROR);
  170. X    }
  171. X                    /* load the file_name array */
  172. X    unfix_name(temp_name);
  173. X                    /* any name collisions? */
  174. X    change_name(win, file_name);
  175. X    return(0);
  176. X}
  177. X
  178. X/*
  179. X * Send the block 0 information for a ymodem batch transfer.  Uses only
  180. X * the name component of the path and the file size.
  181. X */
  182. X
  183. Xint
  184. Xsend_ymodem(win, file, size)
  185. XWINDOW *win;
  186. Xchar *file;
  187. Xint size;
  188. X{
  189. X    int i, crc;
  190. X    char *strcpy();
  191. X    unsigned char buf[133];
  192. X                    /* start with a clean block */
  193. X    for (i=0; i<132; i++)
  194. X        buf[i] = NULL;
  195. X                    /* the header */
  196. X    buf[0] = SOH;
  197. X    buf[1] = 0;
  198. X    buf[2] = 255;
  199. X
  200. X    /*
  201. X     * The block zero consists of the file name (no path component),
  202. X     * a NULL, and the file length (as a string).  The end of batch
  203. X     * marker is an empty block.
  204. X     */
  205. X    if (file != NULL) {
  206. X        strcpy((char *) &buf[3], file);
  207. X        sprintf((char *) &buf[strlen(file)+4], "%d", size);
  208. X    }
  209. X                    /* the crc */
  210. X    crc = calc_crc(&buf[3], 128);
  211. X    buf[131] = crc >> 8;
  212. X    buf[132] = crc;
  213. X                    /* the block count */
  214. X    mvwaddstr(win, 7, 24, "0   ");
  215. X
  216. X    return(send_block(win, buf, 133));
  217. X}
  218. X
  219. X/*
  220. X * Receive the block 0 information for a ymodem batch transfer.  We
  221. X * only use the file name and the size (if present).  Currently doesn't
  222. X * support full path names.
  223. X */
  224. X
  225. Xint
  226. Xrcv_ymodem(win)
  227. XWINDOW *win;
  228. X{
  229. X    int code, length_is_at;
  230. X    extern unsigned char buf[1029];
  231. X    extern int file_length;
  232. X    extern char file_name[15];
  233. X
  234. X    file_length = 0;
  235. X    file_name[0] = NULL;
  236. X                    /* read the zero block */
  237. X    if (code = rcv_block(win, 1, 1024, 0))
  238. X        return(code);
  239. X                    /* at end of batch */
  240. X    if (buf[3] == NULL)
  241. X        return(0);
  242. X                    /* get the file name */
  243. X    change_name(win, (char *) &buf[3]);
  244. X                    /* any trouble ?? */
  245. X    if (file_name[0] == NULL) {
  246. X        putc_line(CAN);
  247. X        return(0);
  248. X    }
  249. X    /*
  250. X     * The file length is placed after the NULL of the file name
  251. X     * and is terminated by another NULL.  If the length is missing,
  252. X     * atoi() will see a NULL and return 0.
  253. X     */
  254. X    length_is_at = strlen((char *) &buf[3]) + 4;
  255. X    file_length = atoi((char *) &buf[length_is_at]);
  256. X    return(0);
  257. X}
  258. X
  259. X/*
  260. X * Handle file name collisions.  Prepend an 'X' to the name until you find
  261. X * a name that doesn't already exist.  Creates a NULL name on error.
  262. X * Loads the global character array 'file_name'.
  263. X */
  264. X
  265. Xvoid
  266. Xchange_name(win, str)
  267. XWINDOW *win;
  268. Xchar *str;
  269. X{
  270. X    int i, modified;
  271. X    char temp[15], ans[15], *s, *strrchr(), *strcpy(), *strncat();
  272. X    unsigned int sleep();
  273. X    extern char file_name[15];
  274. X                    /* dissect the name component */
  275. X    if ((s = strrchr(str, '/')))
  276. X        strcpy(temp, s++);
  277. X    else
  278. X        strcpy(temp, str);
  279. X
  280. X    strcpy(ans, temp);
  281. X    file_name[0] = NULL;
  282. X                    /* write permission on directory ? */
  283. X    if (access(".", 2)) {
  284. X        beep();
  285. X        clear_line(win, 12, 24, 1);
  286. X        wattrstr(win, A_BOLD, "NO WRITE ON DIRECTORY");
  287. X        wrefresh(win);
  288. X        return;
  289. X    }
  290. X                    /* prepend up to 13 'X's */
  291. X    modified = 0;
  292. X    for (i=1; i<14; i++) {
  293. X        if (access(ans, 0)) {
  294. X            if (modified) {
  295. X                beep();
  296. X                clear_line(win, 12, 24, 1);
  297. X                waddstr(win, "NAME COLLISION");
  298. X                wrefresh(win);
  299. X                sleep(1);
  300. X            }
  301. X            strcpy(file_name, ans);
  302. X            return;
  303. X        }
  304. X
  305. X        modified++;
  306. X        strcpy(temp, "X");
  307. X        strncat(temp, ans, 13);
  308. X        temp[14] = NULL;
  309. X        strcpy(ans, temp);
  310. X    }
  311. X    beep();
  312. X    clear_line(win, 12, 24, 1);
  313. X    waddstr(win, "BAD NAME");
  314. X    wrefresh(win);
  315. X    return;
  316. X}
  317. X
  318. X/*
  319. X * Convert a perfectly good Unix file name to fit the CP/M file name
  320. X * rules.  Used for the modem7 batch file transfer.  Returns a pointer
  321. X * to the new name.
  322. X */
  323. X
  324. Xchar *
  325. Xfix_name(path)
  326. Xchar *path;
  327. X{
  328. X    int dot;
  329. X    char *s, *name, temp[15], *ext, *strcpy(), *strrchr();
  330. X    static char ans[13];
  331. X                     /* ignore the path component */
  332. X    if (s = strrchr(path, '/'))
  333. X        strcpy(temp, s++);
  334. X    else
  335. X        strcpy(temp, path);
  336. X    name = temp;
  337. X
  338. X    ext = NULL;
  339. X    dot = 0;
  340. X    for (s = name; *s; ++s) {
  341. X        if (*s == '.' && !dot) {
  342. X            dot = 1;
  343. X            *s = NULL;
  344. X            ext = s + 1;
  345. X        }
  346. X        if (islower(*s))
  347. X            *s = toupper(*s);
  348. X    }
  349. X                    /* if null name component */
  350. X    if (*name == NULL)
  351. X        name = "X";
  352. X                    /* if name too long */
  353. X    if (strlen(name) > 8) 
  354. X        *(name+8) = NULL;
  355. X                    /* if extension too long */
  356. X    if (strlen(ext) > 3) 
  357. X        *(ext+3) = NULL;
  358. X
  359. X    sprintf(ans, "%-8.8s%-3.3s%c", temp, ext, CTRLZ);
  360. X    return(ans);
  361. X}
  362. X
  363. X/*
  364. X * Convert a CP/M style filename into a legal Unix file name.  Loads the
  365. X * global character array 'file_name'.
  366. X */
  367. X
  368. Xvoid
  369. Xunfix_name(cpm_name)
  370. Xchar *cpm_name;
  371. X{
  372. X    int i, n, dot;
  373. X    char temp[15];
  374. X    extern char file_name[15];
  375. X
  376. X    file_name[0] = NULL;
  377. X    if (!*cpm_name)
  378. X        return;
  379. X
  380. X    strcpy(temp, cpm_name);
  381. X                    /* 8 character of the name */
  382. X    n = 0;
  383. X    for (i=0; i<8; i++) {
  384. X        if (temp[i] != ' ') {
  385. X            if (isupper(temp[i]))
  386. X                file_name[n++] = tolower(temp[i]);
  387. X            else
  388. X                file_name[n++] = temp[i];
  389. X        }
  390. X    }
  391. X                    /* 3 character extension */
  392. X    dot = 0;
  393. X    for (i=8; i<11; i++) {
  394. X        if (temp[i] != ' ') {
  395. X            if (!dot) {
  396. X                dot++;
  397. X                file_name[n++] = '.';
  398. X            }
  399. X            if (isupper(temp[i]))
  400. X                file_name[n++] = tolower(temp[i]);
  401. X            else
  402. X                file_name[n++] = temp[i];
  403. X        }
  404. X    }
  405. X    file_name[n] = NULL;
  406. X    return;
  407. X}
  408. SHAR_EOF
  409. if test 7780 -ne "`wc -c < 'x_batch.c'`"
  410. then
  411.     echo shar: "error transmitting 'x_batch.c'" '(should have been 7780 characters)'
  412. fi
  413. fi
  414. echo shar: "extracting 'x_menu.c'" '(4000 characters)'
  415. if test -f 'x_menu.c'
  416. then
  417.     echo shar: "will not over-write existing file 'x_menu.c'"
  418. else
  419. sed 's/^X//' << \SHAR_EOF > 'x_menu.c'
  420. X/*
  421. X * Open a window to display the choices of file transfer protocols and
  422. X * prompt for the file name(s).  A return code of 1 means turn the
  423. X * input routine back on.
  424. X */
  425. X
  426. X#include <stdio.h>
  427. X#include <curses.h>
  428. X#include "misc.h"
  429. X#include "xmodem.h"
  430. X
  431. Xint
  432. Xxfer_menu(up)
  433. Xint up;
  434. X{
  435. X    WINDOW *xm_win, *newwin();
  436. X    char *list, *get_names();
  437. X    int type, is_batch;
  438. X    extern char *null_ptr;
  439. X    void xfer_win(), xfer_ascii(), free_ptr();
  440. X
  441. X    xm_win = newwin(15, 20, 2, 45);
  442. X
  443. X    mvwaddstr(xm_win, 2, 3, "1) xmodem");
  444. X    mvwaddstr(xm_win, 3, 3, "2) xmodem-1k");
  445. X    mvwaddstr(xm_win, 4, 3, "3) modem7");
  446. X    mvwaddstr(xm_win, 5, 3, "4) ymodem");
  447. X    mvwaddstr(xm_win, 6, 3, "5) ymodem-g");
  448. X    mvwaddstr(xm_win, 7, 3, "6) ASCII");
  449. X    mvwaddstr(xm_win, 11, 3, "ESC to Abort");
  450. X    mvwaddstr(xm_win, 13, 3, "Protocol:");
  451. X    box(xm_win, '|', '-');
  452. X    if (up)
  453. X        mvwattrstr(xm_win, 0, 6, A_BOLD, " Upload ");
  454. X    else
  455. X        mvwattrstr(xm_win, 0, 5, A_BOLD, " Download ");
  456. X
  457. X    wmove(xm_win, 13, 13);
  458. X    wrefresh(xm_win);
  459. X                    /* get the protocol */
  460. X    while ((type = get_num(xm_win, 1)) != -1) {
  461. X        if (type >=1 && type <= PROTOCOLS)
  462. X            break;
  463. X        beep();
  464. X        mvwaddch(xm_win, 13, 13, ' ');
  465. X        wmove(xm_win, 13, 13);
  466. X        wrefresh(xm_win);
  467. X    }
  468. X                    /* is a batch protocol ? */
  469. X    is_batch = 0;
  470. X    switch (type-1) {
  471. X        case MODEM7:
  472. X        case YMODEM:
  473. X        case YMODEM_G:
  474. X            is_batch++;
  475. X            break;
  476. X        default:
  477. X            break;
  478. X    }
  479. X    werase(xm_win);
  480. X    wrefresh(xm_win);
  481. X    delwin(xm_win);
  482. X
  483. X    touchwin(stdscr);
  484. X    refresh();
  485. X
  486. X    if (type == -1)
  487. X        return(0);
  488. X    type--;
  489. X
  490. X    /*
  491. X     * When receiving files in one of the batch modes, there is
  492. X     * need to prompt for a list of file names.
  493. X     */
  494. X    list = null_ptr;
  495. X    if (up || !is_batch) {
  496. X        if (!(list = get_names(up, type, is_batch)))
  497. X            return(0);
  498. X    }
  499. X                    /* if ascii transfer */
  500. X    if (type == XASCII) {
  501. X        xfer_ascii(list, up);
  502. X        free_ptr(list);
  503. X        if (up)
  504. X            return(0);
  505. X        return(1);
  506. X    }
  507. X    xfer_win(list, up, type);
  508. X    free_ptr(list);
  509. X    return(1);
  510. X}
  511. X
  512. X/*
  513. X * Prompt for a list of files for the transfer programs.  A NULL return
  514. X * code means you chickened out.
  515. X */
  516. X
  517. Xchar *
  518. Xget_names(up, type, is_batch)
  519. Xint up, type, is_batch;
  520. X{
  521. X    int can;
  522. X    WINDOW *gn_win, *newwin();
  523. X    char *ans, *list, *file, buf[40], *expand(), *get_str(), *strtok();
  524. X    static char *direction[2] = {"Receive", "Send"};
  525. X    static char *protocol[PROTOCOLS] = {"xmodem", "xmodem-1k", "modem7",
  526. X    "ymodem", "ymodem-g", "ASCII"};
  527. X                    /* prompt for file spec */
  528. X    gn_win = newwin(7, 70, 5, 5);
  529. X
  530. X    mvwaddstr(gn_win, 3, 4, "Enter filename: ");
  531. X    box(gn_win, '|', '-');
  532. X    sprintf(buf, " %s %s ", direction[up], protocol[type]);
  533. X
  534. X    while (1) {
  535. X        mvwattrstr(gn_win, 0, 3, A_BOLD, buf);
  536. X        wmove(gn_win, 3, 20);
  537. X        wrefresh(gn_win);
  538. X                    /* get the answers */
  539. X        if (is_batch)
  540. X            ans = get_str(gn_win, 60, NULL, NULL);
  541. X        else
  542. X            ans = get_str(gn_win, 60, NULL, "     ");
  543. X
  544. X        if (ans == NULL || *ans == NULL) {
  545. X            list = NULL;
  546. X            break;
  547. X        }
  548. X        list = expand(ans);
  549. X                    /* batchs check "on the fly" */
  550. X        if (is_batch)
  551. X            break;
  552. X        /*
  553. X         * The non-batch protocols don't check read and write
  554. X         * permission on the fly, so we check 'em here.  Since
  555. X         * they aren't batch, we use only one file.
  556. X         */
  557. X        file = strtok(list, "     ");
  558. X                    /* check read permission */
  559. X        if (up) {
  560. X            if (access(file, 4)) {
  561. X                beep();
  562. X                mvwattrstr(gn_win, 4, 15, A_BOLD, "Can't find or no read permission");
  563. X                wrefresh(gn_win);
  564. X                wait_key(gn_win, 3);
  565. X                clear_line(gn_win, 4, 15, 1);
  566. X                clear_line(gn_win, 3, 20, 1);
  567. X                wrefresh(gn_win);
  568. X            }
  569. X            else
  570. X                break;
  571. X        }
  572. X                    /* check write permission */
  573. X        else {
  574. X            if (!(can = can_write(file))) {
  575. X                beep();
  576. X                clear_line(gn_win, 4, 15, 1);
  577. X                mvwattrstr(gn_win, 4, 15, A_BOLD, "No write permission");
  578. X                wrefresh(gn_win);
  579. X                wait_key(gn_win, 3);
  580. X                clear_line(gn_win, 4, 15, 1);
  581. X                clear_line(gn_win, 3, 20, 1);
  582. X                wrefresh(gn_win);
  583. X            }
  584. X            if (can == 2) {
  585. X                if (!yes_prompt(gn_win, 4, 15, A_BOLD, "File exists, overwrite")) {
  586. X                    list = NULL;
  587. X                    break;
  588. X                }
  589. X            }
  590. X            if (can)
  591. X                break;
  592. X        }
  593. X    }
  594. X    werase(gn_win);
  595. X    wrefresh(gn_win);
  596. X    delwin(gn_win);
  597. X
  598. X    touchwin(stdscr);
  599. X    refresh();
  600. X    return(list);
  601. X}
  602. SHAR_EOF
  603. if test 4000 -ne "`wc -c < 'x_menu.c'`"
  604. then
  605.     echo shar: "error transmitting 'x_menu.c'" '(should have been 4000 characters)'
  606. fi
  607. fi
  608. echo shar: "extracting 'x_rcv.c'" '(10526 characters)'
  609. if test -f 'x_rcv.c'
  610. then
  611.     echo shar: "will not over-write existing file 'x_rcv.c'"
  612. else
  613. sed 's/^X//' << \SHAR_EOF > 'x_rcv.c'
  614. X/*
  615. X * Receive a list of files using a version of Ward Christensen's file
  616. X * transfer protocol.  A return code of 1 means the user must acknowledge
  617. X * an error condition.
  618. X */
  619. X
  620. X#include <stdio.h>
  621. X#include <curses.h>
  622. X#include "dial_dir.h"
  623. X#include "misc.h"
  624. X#include "xmodem.h"
  625. X
  626. Xunsigned char buf[1029];
  627. Xchar file_name[15];
  628. Xint file_length, err_method, tot_err, block_size;
  629. X
  630. Xint
  631. Xrcv_xmodem(win, list, type, fast)
  632. XWINDOW *win;
  633. Xchar *list;
  634. Xint type, fast;
  635. X{
  636. X    FILE *fp;
  637. X    int default_err, is_batch, max_block, code, file_count, i, got_hdr;
  638. X    int block, recv, hours, mins, secs;
  639. X    float percent, performance;
  640. X    unsigned char blk;
  641. X    unsigned int sleep();
  642. X    char *file, *name, *strcpy(), *strrchr(), *strtok();
  643. X    void cancel_xfer();
  644. X
  645. X    /*
  646. X     * What type of xmodem?  The default_err means:  0=checksum only,
  647. X     * 1=CRC or checksum, 2=CRC only, and 3=none.
  648. X     */
  649. X    switch(type) {
  650. X        case XMODEM:
  651. X            mvwaddstr(win, 2, 24, "xmodem");
  652. X            default_err = 1;
  653. X            is_batch = 0;
  654. X            max_block = 128;
  655. X            break;
  656. X        case XMODEM_1k:
  657. X            mvwaddstr(win, 2, 24, "xmodem-1k");
  658. X            default_err = 1;
  659. X            is_batch = 0;
  660. X            max_block = 1024;
  661. X            break;
  662. X        case MODEM7:
  663. X            mvwaddstr(win, 2, 24, "modem7");
  664. X            default_err = 0;
  665. X            is_batch = 1;
  666. X            max_block = 128;
  667. X            break;
  668. X        case YMODEM:
  669. X            mvwaddstr(win, 2, 24, "ymodem");
  670. X            default_err = 2;
  671. X            is_batch = 1;
  672. X            max_block = 1024;
  673. X            performance = 1.09;
  674. X            break;
  675. X        case YMODEM_G:
  676. X            mvwaddstr(win, 2, 24, "ymodem-g");
  677. X            default_err = 3;
  678. X            is_batch = 1;
  679. X            max_block = 1024;
  680. X            performance = 1.02;
  681. X            break;
  682. X    }
  683. X
  684. X    tot_err = 0;
  685. X    file_count = 0;
  686. X    mvwaddstr(win, 11, 24, "0  ");
  687. X
  688. X    while (1) {
  689. X        file_count++;
  690. X        file_length = 0;
  691. X                    /* user supplied name */
  692. X        if (!is_batch) {
  693. X            if (file_count > 1)
  694. X                break;
  695. X
  696. X            file = strtok(list, "     ");
  697. X                    /* dissect the file name */
  698. X            if ((name = strrchr(file, '/')))
  699. X                strcpy(file_name, name++);
  700. X            else
  701. X                strcpy(file_name, file);
  702. X        }
  703. X                    /* get the modem7 file name */
  704. X        if (type == MODEM7) {
  705. X            if (code = rcv_modem7(win, default_err))
  706. X                return(code +1);
  707. X
  708. X            file = file_name;
  709. X        }
  710. X                    /* get the block 0 */
  711. X        if (type == YMODEM || type == YMODEM_G) {
  712. X            if (code = send_first(win, max_block, default_err))
  713. X                return(code +1);
  714. X
  715. X            if (code = rcv_ymodem(win))
  716. X                return(code +1);
  717. X
  718. X                    /* at the end? */
  719. X            if (buf[3] == NULL) {
  720. X                beep();
  721. X                wrefresh(win);
  722. X                putc_line(ACK);
  723. X                sleep(1);
  724. X                return(0);
  725. X            }
  726. X            file = file_name;
  727. X        }
  728. X                    /* any trouble ? */
  729. X        if (file_name[0] == NULL)
  730. X            continue;
  731. X
  732. X        clear_line(win, 3, 24, 1);
  733. X        waddstr(win, file_name);
  734. X                    /* if file length is known */
  735. X        if (file_length) {
  736. X            mvwprintw(win, 4, 24, "%-10d", file_length);
  737. X
  738. X            secs = (file_length * 10.0 / dir->baud[dir->d_cur]) * performance;
  739. X            hours = secs / 3600;
  740. X            mins = (secs % 3600) / 60;
  741. X            secs = (secs % 3600) % 60;
  742. X
  743. X            mvwprintw(win, 6, 24, "%d:%02d:%02d", hours, mins, secs);
  744. X        }
  745. X                    /* some starting numbers */
  746. X        mvwaddstr(win, 7, 24, "0    ");
  747. X        if (file_length && fast)
  748. X            mvwaddstr(win, 8, 24, "0%  ");
  749. X        if (fast)
  750. X            mvwaddstr(win, 9, 24, "0          ");
  751. X        mvwaddstr(win, 10, 24, "0 ");
  752. X        clear_line(win, 12, 24, 1);
  753. X        waddstr(win, "NONE");
  754. X        wrefresh(win);
  755. X
  756. X        /*
  757. X         * If the user supplied the name, write permission is checked
  758. X         * by the get_names() routine in xfer_menu().  If a modem7
  759. X         * or ymodem supplied name, the permission is checked by the
  760. X         * rcv_modem7() and rcv_ymodem() routines.
  761. X         */
  762. X                    /* open the file */
  763. X        if (!(fp = fopen(file, "w"))) {
  764. X            beep();
  765. X            clear_line(win, 12, 24, 1);
  766. X            wattrstr(win, A_BOLD, "CAN'T OPEN FILE");
  767. X            wrefresh(win);
  768. X            cancel_xfer();
  769. X            return(1);
  770. X        }
  771. X                    /* ACK the block 0 */
  772. X        if (type == YMODEM || type == YMODEM_G)
  773. X            putc_line(ACK);
  774. X
  775. X        if (code = send_first(win, max_block, default_err)) {
  776. X            fclose(fp);
  777. X            cancel_xfer();
  778. X            return(code +1);
  779. X        }
  780. X                    /* here we go... */
  781. X        blk = 1;
  782. X        block = 1;
  783. X        recv = 0;
  784. X        got_hdr = 1;
  785. X        while (1) {
  786. X            code = rcv_block(win, got_hdr, max_block, blk);
  787. X
  788. X            if (code < 0) {
  789. X                fclose(fp);
  790. X                cancel_xfer();
  791. X                return(code +1);
  792. X            }
  793. X            got_hdr = 0;
  794. X                    /* are we done? */
  795. X            if (buf[0] == EOT) {
  796. X                if (!is_batch) {
  797. X                    beep();
  798. X                    wrefresh(win);
  799. X                    sleep(1);
  800. X                }
  801. X                break;
  802. X            }
  803. X                    /* if not a duplicate block */
  804. X            if (!code) {
  805. X                fwrite((char *) &buf[3], sizeof(buf[0]), block_size, fp);
  806. X                mvwprintw(win, 7, 24, "%-5d", block);
  807. X                if (fast) {
  808. X                    recv += block_size;
  809. X                    mvwprintw(win, 9, 24, "%-10d", recv);
  810. X                }
  811. X                blk++;
  812. X                block++;
  813. X            }
  814. X            /*
  815. X             * If the length is known, give the same status
  816. X             * report as uploading
  817. X             */
  818. X            if (file_length && fast) {
  819. X                percent = recv * 100.0 / file_length;
  820. X                if (percent > 100.0)
  821. X                    percent = 100.0;
  822. X                mvwprintw(win, 8, 24, "%0.1f%%", percent);
  823. X            }
  824. X            wrefresh(win);
  825. X            putc_line(ACK);
  826. X        }
  827. X        if (file_length && fast) {
  828. X            mvwaddstr(win, 8, 24, "100%  ");
  829. X            wrefresh(win);
  830. X        }
  831. X        /*
  832. X         * If the file length is not known, then search backwards
  833. X         * from the end of the file until you find a character that
  834. X         * is not the ^Z padding character
  835. X         */
  836. X        if (!file_length) {
  837. X            for (i=block_size+2; i>2; i--) {
  838. X                if (buf[i] != CTRLZ)
  839. X                    break;
  840. X            }
  841. X            file_length = recv - block_size + i -2;
  842. X        }
  843. X        fclose(fp);
  844. X        fix_length(file_name, file_length);
  845. X                    /* ACK the EOT */
  846. X        putc_line(ACK);
  847. X    }
  848. X    return(0);
  849. X}
  850. X
  851. X/*
  852. X * Send the first character to start the transmission and set the error
  853. X * checking method.  Returns the standard error codes or 0 on success.
  854. X * The variables err_method and block_size are global.
  855. X */
  856. X
  857. Xint
  858. Xsend_first(win, max_block, default_err)
  859. XWINDOW *win;
  860. Xint max_block, default_err;
  861. X{
  862. X    int i, err_count;
  863. X    unsigned int sleep();
  864. X                    /* default error method */
  865. X    err_method = default_err;
  866. X    if (err_method > 1)
  867. X        err_method--;
  868. X                    /* send the first char */
  869. X    err_count = 0;
  870. X    while (err_count < MAX_ERRORS*2) {
  871. X        mvwprintw(win, 10, 24, "%-2d", err_count);
  872. X
  873. X                    /* check for keyboard abort */
  874. X        if (wgetch(win) == 27) {
  875. X            beep();
  876. X            clear_line(win, 12, 24, 1);
  877. X            waddstr(win, "ABORTED");
  878. X            wrefresh(win);
  879. X            sleep(3);
  880. X            return(ABORT);
  881. X        }
  882. X                    /* switch to checksum? */
  883. X        if (default_err == 1 && err_count > MAX_ERRORS)
  884. X            err_method = 0;
  885. X
  886. X                    /* send error method code */
  887. X        clear_line(win, 5, 24, 1);
  888. X        switch (err_method) {
  889. X            case 0:
  890. X                waddstr(win, "CHECKSUM");
  891. X                putc_line(NAK);
  892. X                break;
  893. X            case 1:
  894. X                waddstr(win, "CRC");
  895. X                putc_line('C');
  896. X                break;
  897. X            case 2:
  898. X                waddstr(win, "NONE");
  899. X                putc_line('G');
  900. X                break;
  901. X        }
  902. X        /*
  903. X         * We've cut the delay time in half, so we double
  904. X         * the allowable errors
  905. X         */
  906. X        if ((i = getc_line(5)) == -1) {
  907. X            err_count++;
  908. X            clear_line(win, 12, 24, 1);
  909. X            waddstr(win, "NO RESPONSE");
  910. X            wrefresh(win);
  911. X            continue;
  912. X        }
  913. X        buf[0] = i;
  914. X
  915. X        switch(buf[0]) {
  916. X            case SOH:    /* small block follows */
  917. X                block_size = 128;
  918. X                return(0);
  919. X            case STX:    /* large block follows */
  920. X                if (max_block == 1024) {
  921. X                    block_size = 1024;
  922. X                    return(0);
  923. X                }
  924. X                /* fall thru */
  925. X            default:
  926. X                err_count++;
  927. X                clear_line(win, 12, 24, 1);
  928. X                waddstr(win, "BAD HEADER");
  929. X                wrefresh(win);
  930. X                    /* read some garbage... */
  931. X                fread_line(buf, 1028, 3);
  932. X                putc_line(NAK);
  933. X                break;
  934. X        }
  935. X    }
  936. X    beep();
  937. X    clear_line(win, 12, 24, 1);
  938. X    wattrstr(win, A_BOLD, "TIMED OUT");
  939. X    wrefresh(win);
  940. X    return(ERROR);
  941. X}
  942. X
  943. X/*
  944. X * Receive a block of info from the host.  Returns a 0 on success, a 1 on
  945. X * a duplicate block or the standard error codes.   The variables 
  946. X * err_method and block_size are global.
  947. X */
  948. X
  949. Xint
  950. Xrcv_block(win, got_hdr, max_block, blk)
  951. XWINDOW *win;
  952. Xint got_hdr, max_block;
  953. Xunsigned char blk;
  954. X{
  955. X    int i, err_count, bad_block, out_of_sync, crc;
  956. X    unsigned int packet, sleep();
  957. X    unsigned char calc_sum(), crc_1, crc_2;
  958. X
  959. X    err_count = 0;
  960. X    while (err_count < MAX_ERRORS) {
  961. X        mvwprintw(win, 10, 24, "%-2d", err_count);
  962. X        mvwprintw(win, 11, 24, "%-3d", tot_err);
  963. X
  964. X                    /* scan the keyboard for abort */
  965. X        if (wgetch(win) == 27) {
  966. X            beep();
  967. X            clear_line(win, 12, 24, 1);
  968. X            waddstr(win, "ABORTED");
  969. X            wrefresh(win);
  970. X            sleep(3);
  971. X            return(ABORT);
  972. X        }
  973. X                    /* have we already got a hdr? */
  974. X        if (!got_hdr) {
  975. X            if ((i = getc_line(10)) == -1) {
  976. X                err_count++;
  977. X                tot_err++;
  978. X                clear_line(win, 12, 24, 1);
  979. X                waddstr(win, "NO RESPONSE");
  980. X                wrefresh(win);
  981. X                continue;
  982. X            }
  983. X            buf[0] = i;
  984. X                    /* what'd we get? */
  985. X            switch(buf[0]) {
  986. X                case EOT:    /* we're done! */
  987. X                    clear_line(win, 12, 24, 1);
  988. X                    waddstr(win, "TRANSFER COMPLETE");
  989. X                    wrefresh(win);
  990. X                    sleep(1);
  991. X                    return(0);
  992. X                case SOH:    /* small block follows */
  993. X                    block_size = 128;
  994. X                    break;
  995. X                case STX:    /* large block follows */
  996. X                    if (max_block == 1024) {
  997. X                        block_size = 1024;
  998. X                        break;
  999. X                    }
  1000. X                    /* fall thru... */
  1001. X                default:
  1002. X                    err_count++;
  1003. X                    tot_err++;
  1004. X                    clear_line(win, 12, 24, 1);
  1005. X                    waddstr(win, "BAD HEADER");
  1006. X                    wrefresh(win);
  1007. X
  1008. X                    /* flush a bad packet */
  1009. X                    fread_line(buf, 1028, 5);
  1010. X                    putc_line(NAK);
  1011. X                    continue;
  1012. X            }
  1013. X        }
  1014. X        got_hdr = 0;
  1015. X                    /* read the rest of the packet */
  1016. X        packet = block_size + 2 + (err_method ? 2 : 1);
  1017. X        if (fread_line(&buf[1], packet, 10) == -1) {
  1018. X            clear_line(win, 12, 24, 1);
  1019. X            waddstr(win, "TIMED OUT");
  1020. X            wrefresh(win);
  1021. X            putc_line(NAK);
  1022. X            err_count++;
  1023. X            tot_err++;
  1024. X            continue;
  1025. X        }
  1026. X
  1027. X        /*
  1028. X         * Validation of the packet includes checking the
  1029. X         * block number, its complement, and the crc/checksum.
  1030. X         */
  1031. X        out_of_sync = 0;
  1032. X        if (buf[1] != blk || buf[2] != (unsigned char) ~blk)
  1033. X            out_of_sync++;
  1034. X
  1035. X        bad_block = 0;
  1036. X        switch(err_method) {
  1037. X            case 0:        /* checksum */
  1038. X                if (buf[block_size +3] != calc_sum(&buf[3], block_size))
  1039. X                    bad_block++;
  1040. X                break;
  1041. X            case 1:        /* CRC */
  1042. X                crc = calc_crc(&buf[3], block_size);
  1043. X                crc_1 = crc >> 8;
  1044. X                crc_2 = crc;
  1045. X                if (buf[block_size +3] != crc_1 || buf[block_size +4] != crc_2)
  1046. X                    bad_block++;
  1047. X                break;
  1048. X            case 2:        /* none */
  1049. X                return(0);
  1050. X        }
  1051. X                    /* handle errors */
  1052. X        if (bad_block) {
  1053. X            clear_line(win, 12, 24, 1);
  1054. X            if (err_method)
  1055. X                waddstr(win, "CRC FAILED");
  1056. X            else
  1057. X                waddstr(win, "CHECKSUM FAILED");
  1058. X            wrefresh(win);
  1059. X            putc_line(NAK);
  1060. X            err_count++;
  1061. X            tot_err++;
  1062. X            continue;
  1063. X        }
  1064. X                    /* not really an error */
  1065. X        if (out_of_sync) {
  1066. X            /*
  1067. X             * If a perfect packet is off by 1 block number,
  1068. X             * (a lost ACK could cause this) then treat it as
  1069. X             * a good block but don't write it to disk.
  1070. X             */
  1071. X            if (buf[1] == (unsigned char) blk-1)
  1072. X                return(1);
  1073. X
  1074. X            clear_line(win, 12, 24, 1);
  1075. X            waddstr(win, "OUT OF SYNC");
  1076. X            wrefresh(win);
  1077. X            putc_line(NAK);
  1078. X            err_count++;
  1079. X            tot_err++;
  1080. X            continue;
  1081. X        }
  1082. X        return(0);
  1083. X    }
  1084. X    beep();
  1085. X    clear_line(win, 12, 24, 1);
  1086. X    waddstr(win, "TOO MANY ERRORS");
  1087. X    wrefresh(win);
  1088. X    return(ERROR);
  1089. X}
  1090. SHAR_EOF
  1091. if test 10526 -ne "`wc -c < 'x_rcv.c'`"
  1092. then
  1093.     echo shar: "error transmitting 'x_rcv.c'" '(should have been 10526 characters)'
  1094. fi
  1095. fi
  1096. echo shar: "extracting 'x_send.c'" '(10461 characters)'
  1097. if test -f 'x_send.c'
  1098. then
  1099.     echo shar: "will not over-write existing file 'x_send.c'"
  1100. else
  1101. sed 's/^X//' << \SHAR_EOF > 'x_send.c'
  1102. X/*
  1103. X * Send a list of files using a version of Ward Christensen's file
  1104. X * transfer protocol.  A non-zero return code means an error must be
  1105. X * acknowledged by the user.
  1106. X */
  1107. X
  1108. X#include <stdio.h>
  1109. X#include <curses.h>
  1110. X#include <sys/types.h>
  1111. X#include <sys/stat.h>
  1112. X#include "dial_dir.h"
  1113. X#include "status.h"
  1114. X#include "misc.h"
  1115. X#include "xmodem.h"
  1116. X
  1117. Xint tot_err, err_method;
  1118. X
  1119. Xint
  1120. Xsend_xmodem(win, list, type, fast)
  1121. XWINDOW *win;
  1122. Xchar *list;
  1123. Xint type, fast;
  1124. X{
  1125. X    FILE *fp;
  1126. X    int i, block_size, file_count, secs, mins, hours, size, xmit_size;
  1127. X    int big_blocks, small_blocks, err_count, got_it, num, block, sent;
  1128. X    int crc, is_batch, code, max_block, default_err;
  1129. X    char *file, *strtok(), *name, *strrchr();
  1130. X    unsigned char buf[1029], blk, calc_sum();
  1131. X    unsigned int packet, sleep();
  1132. X    float performance, percent;
  1133. X    struct stat sbuf;
  1134. X
  1135. X    /*
  1136. X     * What type of xmodem?  The default_err means: 0=checksum only,
  1137. X     * 1=checksum or CRC, 2=CRC only, and 3=none.
  1138. X     */
  1139. X    switch(type) {
  1140. X        case XMODEM:
  1141. X            mvwaddstr(win, 2, 24, "xmodem");
  1142. X            is_batch = 0;
  1143. X            default_err = 1;
  1144. X            max_block = 128;
  1145. X            performance = 1.36;
  1146. X            break;
  1147. X        case XMODEM_1k:
  1148. X            mvwaddstr(win, 2, 24, "xmodem-1k");
  1149. X            is_batch = 0;
  1150. X            default_err = 1;
  1151. X            max_block = 1024;
  1152. X            performance = 1.09;
  1153. X            break;
  1154. X        case MODEM7:
  1155. X            mvwaddstr(win, 2, 24, "modem7");
  1156. X            is_batch = 1;
  1157. X            default_err = 0;
  1158. X            max_block = 128;
  1159. X            performance = 1.36;
  1160. X            break;
  1161. X        case YMODEM:
  1162. X            mvwaddstr(win, 2, 24, "ymodem");
  1163. X            is_batch = 1;
  1164. X            default_err = 2;
  1165. X            max_block = 1024;
  1166. X            performance = 1.09;
  1167. X            break;
  1168. X        case YMODEM_G:
  1169. X            mvwaddstr(win, 2, 24, "ymodem-g");
  1170. X            is_batch = 1;
  1171. X            default_err = 3;
  1172. X            max_block = 1024;
  1173. X            performance = 1.02;
  1174. X            break;
  1175. X    }
  1176. X
  1177. X    tot_err = 0;
  1178. X    file_count = 0;
  1179. X    mvwaddstr(win, 11, 24, "0  ");
  1180. X
  1181. X                    /* each one in the list */
  1182. X    file = strtok(list, "     ");
  1183. X    do {
  1184. X                    /* is it a batch type ? */
  1185. X        file_count++;
  1186. X        if (file_count > 1 && !is_batch)
  1187. X            break;
  1188. X                    /* display the name */
  1189. X        clear_line(win, 3, 24, 1);
  1190. X        if ((name = strrchr(file, '/')))
  1191. X            name++;
  1192. X        else
  1193. X            name = file;
  1194. X        waddstr(win, name);
  1195. X        wrefresh(win);
  1196. X                    /* get the file size */    
  1197. X        if (stat(file, &sbuf) < 0) {
  1198. X            beep();
  1199. X            clear_line(win, 12, 24, 1);
  1200. X            wattrstr(win, A_BOLD, "CAN'T FIND FILE");
  1201. X            wrefresh(win);
  1202. X            sleep(3);
  1203. X            continue;
  1204. X        }
  1205. X
  1206. X        size = sbuf.st_size;
  1207. X        mvwprintw(win, 4, 24, "%-10d", size);
  1208. X        clear_line(win, 5, 24, 1);
  1209. X
  1210. X        if (access(file, 4)) {
  1211. X            beep();
  1212. X            clear_line(win, 12, 24, 1);
  1213. X            wattrstr(win, A_BOLD, "PERMISSION DENIED");
  1214. X            wrefresh(win);
  1215. X            sleep(3);
  1216. X            continue;
  1217. X        }
  1218. X        if (!(fp = fopen(file, "r"))) {
  1219. X            beep();
  1220. X            clear_line(win, 12, 24, 1);
  1221. X            wattrstr(win, A_BOLD, "CAN'T OPEN FILE");
  1222. X            wrefresh(win);
  1223. X            sleep(3);
  1224. X            continue;
  1225. X        }
  1226. X                    /* get the xmit size */
  1227. X        block_size = max_block;
  1228. X        big_blocks = 0;
  1229. X        small_blocks = 0;
  1230. X        if (block_size == 128) {
  1231. X            small_blocks = size / 128;
  1232. X            if (size % 128)
  1233. X                small_blocks++;
  1234. X        }
  1235. X        else {
  1236. X            big_blocks = size / 1024;
  1237. X            small_blocks = (size % 1024) / 128;
  1238. X            if (size % 128)
  1239. X                small_blocks++;
  1240. X
  1241. X            if (small_blocks == 8 && !big_blocks) {
  1242. X                big_blocks++;
  1243. X                small_blocks = 0;
  1244. X            }
  1245. X                    /* if tiny file */
  1246. X            if (big_blocks == 0)
  1247. X                block_size = 128;
  1248. X        }
  1249. X
  1250. X        xmit_size = (big_blocks * 1024) + (small_blocks * 128);
  1251. X                    /* add block 0 to the size */
  1252. X        if (type == YMODEM || type == YMODEM_G)
  1253. X            xmit_size += 128;
  1254. X
  1255. X        secs = (xmit_size * 10.0 / dir->baud[dir->d_cur]) * performance;
  1256. X        hours = secs / 3600;
  1257. X        mins = (secs % 3600) / 60;
  1258. X        secs = (secs % 3600) % 60;
  1259. X
  1260. X        mvwprintw(win, 6, 24, "%d:%02d:%02d", hours, mins, secs);
  1261. X
  1262. X                    /* some starting numbers */
  1263. X        mvwaddstr(win, 7, 24, "     ");
  1264. X        mvwaddstr(win, 8, 24, "0%  ");
  1265. X        mvwaddstr(win, 9, 24, "0          ");
  1266. X        mvwaddstr(win, 10, 24, "0 ");
  1267. X        clear_line(win, 12, 24, 1);
  1268. X        waddstr(win, "NONE");
  1269. X        wrefresh(win);
  1270. X                    /* send the batch stuff */
  1271. X        switch (type) {
  1272. X            case MODEM7:
  1273. X                if (code = rcv_first(win, default_err)) {
  1274. X                    fclose(fp);
  1275. X                    return(code +1);
  1276. X                }
  1277. X
  1278. X                if (send_modem7(win, name)) {
  1279. X                    fclose(fp);
  1280. X                    return(1);
  1281. X                }
  1282. X                break;
  1283. X            case YMODEM:
  1284. X            case YMODEM_G:
  1285. X                if (code = rcv_first(win, default_err)) {
  1286. X                    fclose(fp);
  1287. X                    return(code +1);
  1288. X                }
  1289. X
  1290. X                if (code = send_ymodem(win, name, size)) {
  1291. X                    fclose(fp);
  1292. X                    /*
  1293. X                     * CANCEL now means that the other
  1294. X                     * end can't open that file.
  1295. X                     */
  1296. X                    if (code == CANCEL)
  1297. X                        break;
  1298. X                    return(code +1);
  1299. X                }
  1300. X                xmit_size -= 128;
  1301. X                break;
  1302. X            default:
  1303. X                code = 0;
  1304. X                break;
  1305. X        }
  1306. X                    /* remote can't receive that file ? */
  1307. X        if (code == CANCEL)
  1308. X            break;
  1309. X                    /* wait for first character */
  1310. X        if (code = rcv_first(win, default_err)) {
  1311. X            fclose(fp);
  1312. X            return(code +1);
  1313. X        }
  1314. X                    /* here we go... */
  1315. X        sent = 0;
  1316. X        block = 1;
  1317. X        blk = 1;
  1318. X        while (num = fread((char *) &buf[3], sizeof(buf[0]), block_size, fp)) {
  1319. X
  1320. X                    /* fill short block */
  1321. X            if (num < block_size) {
  1322. X                for (i=num; i<block_size; i++)
  1323. X                    buf[i+3] = CTRLZ;
  1324. X            }
  1325. X
  1326. X                    /* show current stats */
  1327. X            mvwprintw(win, 7, 24, "%-5d", block);
  1328. X            if (fast) {
  1329. X                percent = sent * 100.0 / xmit_size;
  1330. X                mvwprintw(win, 8, 24, "%0.1f%%", percent);
  1331. X                mvwprintw(win, 9, 24, "%-10d", sent);
  1332. X            }
  1333. X            wrefresh(win);
  1334. X
  1335. X                    /* build the header */
  1336. X            if (block_size == 128)
  1337. X                buf[0] = SOH;
  1338. X            else
  1339. X                buf[0] = STX;
  1340. X
  1341. X            buf[1] = blk;
  1342. X            buf[2] = ~blk;
  1343. X
  1344. X                    /* build the error detection stuff */
  1345. X            switch (err_method) {
  1346. X                case 0:        /* checksum */
  1347. X                    buf[block_size+3] = calc_sum(&buf[3], block_size);
  1348. X                    packet = block_size +4;
  1349. X                    break;
  1350. X                case 1:        /* CRC */
  1351. X                    crc = calc_crc(&buf[3], block_size);
  1352. X                    buf[block_size+3] = crc >> 8;
  1353. X                    buf[block_size+4] = crc;
  1354. X                    packet = block_size +5;
  1355. X                    break;
  1356. X                case 2:        /* none */
  1357. X                    buf[block_size+3] = 0;
  1358. X                    buf[block_size+4] = 0;
  1359. X                    packet = block_size +5;
  1360. X                    break;
  1361. X            }
  1362. X
  1363. X                    /* send the block */
  1364. X            if (code = send_block(win, buf, packet)) {
  1365. X                fclose(fp);
  1366. X                return(code +1);
  1367. X            }
  1368. X            block++;
  1369. X            blk++;
  1370. X            sent += block_size;
  1371. X
  1372. X                    /* change block size ? */
  1373. X            if (xmit_size - sent < 1024)
  1374. X                block_size = 128;
  1375. X        }
  1376. X        mvwaddstr(win, 8, 24, "100%  ");
  1377. X        mvwprintw(win, 9, 24, "%-10d", sent);
  1378. X                    /* at the end of the file */
  1379. X        err_count = 0;
  1380. X        got_it = 0;
  1381. X        while (err_count < MAX_ERRORS) {
  1382. X            putc_line(EOT);
  1383. X            if (getc_line(10) == ACK) {
  1384. X                got_it++;
  1385. X                break;
  1386. X            }
  1387. X            err_count++;
  1388. X        }
  1389. X        clear_line(win, 12, 24, 1);
  1390. X        if (!got_it) {
  1391. X            /*
  1392. X             * So what???  We don't do anything if there is
  1393. X             * no acknowledge from the host!!
  1394. X             */
  1395. X            waddstr(win, "NO ACKNOWLEDGE");
  1396. X        }
  1397. X        else
  1398. X            waddstr(win, "TRANSFER COMPLETE");
  1399. X        if (!is_batch)
  1400. X            beep();
  1401. X        wrefresh(win);
  1402. X        sleep(2);
  1403. X                    /* prepare to start again */
  1404. X        fclose(fp);
  1405. X    } while (file = strtok((char *) NULL, "     "));
  1406. X
  1407. X    /*
  1408. X     * The end of batch markers... For modem7 it's an ACK and EOT, for
  1409. X     * ymodem, it's an empty block 0.
  1410. X     */
  1411. X    switch (type) {
  1412. X        case MODEM7:
  1413. X            if (code = rcv_first(win, default_err))
  1414. X                return(code +1);
  1415. X            putc_line(ACK);
  1416. X            putc_line(EOT);
  1417. X            beep();
  1418. X            wrefresh(win);
  1419. X            break;
  1420. X        case YMODEM:
  1421. X        case YMODEM_G:
  1422. X            if (code = rcv_first(win, default_err))
  1423. X                return(code +1);
  1424. X
  1425. X            if (code = send_ymodem(win, NULL, 0))
  1426. X                return(code +1);
  1427. X            beep();
  1428. X            wrefresh(win);
  1429. X            break;
  1430. X        default:
  1431. X            break;
  1432. X    }
  1433. X    return(0);
  1434. X}
  1435. X
  1436. X/*
  1437. X * Wait for the first character to start the transmission.  This first
  1438. X * character also sets the crc/checksum method.  Returns the standard
  1439. X * error codes, or 0 on success.  The variable err_method is global.
  1440. X */
  1441. X
  1442. Xint
  1443. Xrcv_first(win, default_err)
  1444. XWINDOW *win;
  1445. Xint default_err;
  1446. X{
  1447. X    int err_count;
  1448. X    unsigned int sleep();
  1449. X
  1450. X    err_count = 0;
  1451. X    while (err_count < MAX_ERRORS) {
  1452. X
  1453. X                    /* scan the keyboard for abort */
  1454. X        if (wgetch(win) == 27) {
  1455. X            beep();
  1456. X            clear_line(win, 12, 24, 1);
  1457. X            waddstr(win, "ABORTED");
  1458. X            wrefresh(win);
  1459. X            sleep(3);
  1460. X            return(ABORT);
  1461. X        }
  1462. X
  1463. X                    /* scan the tty line */
  1464. X        switch(getc_line(10)) {
  1465. X            case NAK:    /* checksum marker */
  1466. X                if (default_err < 2) {
  1467. X                    mvwaddstr(win, 5, 24, "CHECKSUM");
  1468. X                    err_method = 0;
  1469. X                    return(0);
  1470. X                }
  1471. X                err_count++;
  1472. X                break;
  1473. X            case 'C':    /* CRC marker */
  1474. X                if (default_err == 1 || default_err == 2) {
  1475. X                    mvwaddstr(win, 5, 24, "CRC");
  1476. X                    err_method = 1;
  1477. X                    return(0);
  1478. X                }
  1479. X                err_count++;
  1480. X                break;
  1481. X            case 'G':    /* ymodem-g marker */
  1482. X                if (default_err == 3) {
  1483. X                    mvwaddstr(win, 5, 24, "NONE");
  1484. X                    err_method = 2;
  1485. X                    return(0);
  1486. X                }
  1487. X                err_count++;
  1488. X                break;
  1489. X            case CAN:    /* two CAN's and you're out! */
  1490. X                if (getc_line(2) == CAN) {
  1491. X                    beep();
  1492. X                    clear_line(win, 12, 24, 1);
  1493. X                    wattrstr(win, A_BOLD, "REMOTE ABORTED");
  1494. X                    wrefresh(win);
  1495. X                    return(CANCEL);
  1496. X                }
  1497. X                /* fall thru... */
  1498. X            default:
  1499. X                err_count++;
  1500. X                break;
  1501. X        }
  1502. X        clear_line(win, 12, 24, 1);
  1503. X        waddstr(win, "BAD HEADER");
  1504. X        mvwprintw(win, 10, 24, "%-2d", err_count);
  1505. X        wrefresh(win);
  1506. X    }
  1507. X                    /* failed to get it right ? */
  1508. X    beep();
  1509. X    clear_line(win, 12, 24, 1);
  1510. X    wattrstr(win, A_BOLD, "TIMED OUT");
  1511. X    wrefresh(win);
  1512. X    return(ERROR);
  1513. X}
  1514. X
  1515. X/*
  1516. X * Send a block of data, scan the keyboard for a user abort, and check
  1517. X * the return codes from the host.  Returns standard error codes or 0
  1518. X * on success.
  1519. X */
  1520. X
  1521. Xint
  1522. Xsend_block(win, blk, packet)
  1523. XWINDOW *win;
  1524. Xunsigned char *blk;
  1525. Xunsigned int packet;
  1526. X{
  1527. X    int err_count;
  1528. X
  1529. X    err_count = 0;
  1530. X    while (err_count < MAX_ERRORS) {
  1531. X                    /* write the block */
  1532. X        write(status->fd, (char *) blk, packet);
  1533. X                    /* scan the keyboard */
  1534. X        if (wgetch(win) == 27) {
  1535. X            beep();
  1536. X            clear_line(win, 12, 24, 1);
  1537. X            waddstr(win, "ABORTED");
  1538. X            wrefresh(win);
  1539. X            sleep(3);
  1540. X            return(ABORT);
  1541. X        }
  1542. X                    /* ymodem-g doesn't need ACKs */
  1543. X        if (err_method == 2)
  1544. X            return(0);
  1545. X                    /* wait for acknowledge */
  1546. X        switch(getc_line(10)) {
  1547. X            case ACK:    /* Hooray!! we got it */
  1548. X                return(0);
  1549. X            case NAK:    /* show our disappointment... */
  1550. X                clear_line(win, 12, 24, 1);
  1551. X                if (err_method)
  1552. X                    waddstr(win, "CRC FAILED");
  1553. X                else
  1554. X                    waddstr(win, "CHECKSUM FAILED");
  1555. X                err_count++;
  1556. X                tot_err++;
  1557. X                break;
  1558. X            case CAN:    /* two CAN's and you're out! */
  1559. X                if (getc_line(2) == CAN) {
  1560. X                    beep();
  1561. X                    clear_line(win, 12, 24, 1);
  1562. X                    wattrstr(win, A_BOLD, "REMOTE ABORTED");
  1563. X                    wrefresh(win);
  1564. X                    return(CANCEL);
  1565. X                }
  1566. X                /* fall thru... */
  1567. X            default:
  1568. X                clear_line(win, 12, 24, 1);
  1569. X                waddstr(win, "RESENDING");
  1570. X                err_count++;
  1571. X                tot_err++;
  1572. X                break;
  1573. X        }
  1574. X        mvwprintw(win, 10, 24, "%-2d", err_count);
  1575. X        mvwprintw(win, 11, 24, "%-3d", tot_err);
  1576. X        wrefresh(win);
  1577. X    }
  1578. X                    /* failed to get it right */
  1579. X    beep();
  1580. X    clear_line(win, 12, 24, 1);
  1581. X    wattrstr(win, A_BOLD, "TOO MANY ERRORS");
  1582. X    wrefresh(win);
  1583. X    return(ERROR);
  1584. X}
  1585. SHAR_EOF
  1586. if test 10461 -ne "`wc -c < 'x_send.c'`"
  1587. then
  1588.     echo shar: "error transmitting 'x_send.c'" '(should have been 10461 characters)'
  1589. fi
  1590. fi
  1591. echo shar: "extracting 'x_win.c'" '(2758 characters)'
  1592. if test -f 'x_win.c'
  1593. then
  1594.     echo shar: "will not over-write existing file 'x_win.c'"
  1595. else
  1596. sed 's/^X//' << \SHAR_EOF > 'x_win.c'
  1597. X/*
  1598. X * Display the file transfer window, and invoke the transfer protocol.
  1599. X */
  1600. X
  1601. X#include <stdio.h>
  1602. X#include <curses.h>
  1603. X#include "dial_dir.h"
  1604. X#include "misc.h"
  1605. X#include "status.h"
  1606. X#include "xmodem.h"
  1607. X
  1608. Xvoid
  1609. Xxfer_win(list, up, type)
  1610. Xchar *list;
  1611. Xint up, type;
  1612. X{
  1613. X    WINDOW *xf_win, *newwin();
  1614. X    int ret_code, fast, my_speed;
  1615. X    void xmodem_mode(), input_off(), line_set(), error_win();
  1616. X    struct termio tbuf;
  1617. X
  1618. X    if (status->fd == -1) {
  1619. X        error_win(0, "Not currently connected to any host", NULL);
  1620. X        return;
  1621. X    }
  1622. X
  1623. X    xf_win = newwin(15, 44, 2, 30);
  1624. X    /*
  1625. X     * This window should be in the non-blocking mode, so we can
  1626. X     * scan the keyboard for input while transferring a file.
  1627. X     */
  1628. X    nodelay(xf_win, 1);
  1629. X                    /* basic window stuff */
  1630. X    mvwaddstr(xf_win, 2, 14, "Protocol:");
  1631. X    mvwaddstr(xf_win, 3, 13, "File name:");
  1632. X    mvwaddstr(xf_win, 4, 13, "File size:");
  1633. X    mvwaddstr(xf_win, 5, 4, "Error check method:");
  1634. X    mvwaddstr(xf_win, 6, 5, "Est transfer time:");
  1635. X    mvwaddstr(xf_win, 7, 11, "Block count:");
  1636. X    mvwaddstr(xf_win, 8, 6, "Percent complete:");
  1637. X    mvwaddstr(xf_win, 9, 5, "Bytes transferred:");
  1638. X    mvwaddstr(xf_win, 10, 5, "Errors this block:");
  1639. X    mvwaddstr(xf_win, 11, 5, "Total error count:");
  1640. X    mvwaddstr(xf_win, 12, 10, "Last message: NONE");
  1641. X    box(xf_win, '|', '-');
  1642. X
  1643. X    if (up)
  1644. X        mvwattrstr(xf_win, 0, 17, A_BOLD, " Uploading ");
  1645. X    else
  1646. X        mvwattrstr(xf_win, 0, 16, A_BOLD, " Downloading ");
  1647. X    mvwaddstr(xf_win, 14, 12, " Press ESC to abort ");
  1648. X    wrefresh(xf_win);
  1649. X                    /* fix up the terminal mode */
  1650. X    input_off();
  1651. X    xmodem_mode(status->fd);
  1652. X
  1653. X    /*
  1654. X     * Is your terminal slower than the xfer baud rate?  For example:
  1655. X     * I'm at home with my PC and 1200 baud modem, I call my system
  1656. X     * at work so I can use their 2400 baud modems to call some other
  1657. X     * system.  In this case, I don't wanna spend too much time updating
  1658. X     * my screen at 1200 baud, when I'm transferring the file at 2400 baud.
  1659. X     */
  1660. X    my_speed = 0;
  1661. X    fast = 0;
  1662. X
  1663. X    ioctl(0, TCGETA, &tbuf);
  1664. X                    /* only reasonable values are here */
  1665. X    switch(tbuf.c_cflag & CBAUD) {
  1666. X        case B300:
  1667. X            my_speed = 300;
  1668. X            break;
  1669. X        case B1200:
  1670. X            my_speed = 1200;
  1671. X            break;
  1672. X        case B2400:
  1673. X            my_speed = 2400;
  1674. X            break;
  1675. X        case B4800:
  1676. X            my_speed = 4800;
  1677. X            break;
  1678. X        case B9600:
  1679. X            my_speed = 9600;
  1680. X            break;
  1681. X        case B19200:
  1682. X            my_speed = 19200;
  1683. X            break;
  1684. X    }
  1685. X    if (my_speed >= dir->baud[dir->d_cur])
  1686. X        fast++;
  1687. X
  1688. X    if (up)
  1689. X        ret_code = send_xmodem(xf_win, list, type, fast);
  1690. X    else
  1691. X        ret_code = rcv_xmodem(xf_win, list, type, fast);
  1692. X
  1693. X    nodelay(xf_win, 0);
  1694. X                    /* prompt for a key on errors */
  1695. X    if (ret_code) {
  1696. X        beep();
  1697. X        clear_line(xf_win, 13, 9, 1);
  1698. X        wattrstr(xf_win, A_BOLD, "Press any key to continue");
  1699. X        wrefresh(xf_win);
  1700. X        wgetch(xf_win);
  1701. X    }
  1702. X    werase(xf_win);
  1703. X    wrefresh(xf_win);
  1704. X    delwin(xf_win);
  1705. X                    /* undo what xmodem_mode() did */
  1706. X    line_set();
  1707. X    return;
  1708. X}
  1709. SHAR_EOF
  1710. if test 2758 -ne "`wc -c < 'x_win.c'`"
  1711. then
  1712.     echo shar: "error transmitting 'x_win.c'" '(should have been 2758 characters)'
  1713. fi
  1714. fi
  1715. echo shar: "extracting 'xmodem.c'" '(4611 characters)'
  1716. if test -f 'xmodem.c'
  1717. then
  1718.     echo shar: "will not over-write existing file 'xmodem.c'"
  1719. else
  1720. sed 's/^X//' << \SHAR_EOF > 'xmodem.c'
  1721. X/*
  1722. X * Miscellaneous routines to support the xmodem file transfer protocols.
  1723. X */
  1724. X
  1725. X#include <stdio.h>
  1726. X#include <signal.h>
  1727. X#include <termio.h>
  1728. X#include <setjmp.h>
  1729. X#include <sys/types.h>
  1730. X#include <sys/stat.h>
  1731. X#include "param.h"
  1732. X#include "status.h"
  1733. X#include "xmodem.h"
  1734. X
  1735. X/*
  1736. X * Calculate the CRC for the given buffer
  1737. X */
  1738. X
  1739. Xint
  1740. Xcalc_crc(buf, len)
  1741. Xunsigned char *buf;
  1742. Xint len;
  1743. X{
  1744. X    int crc, i;
  1745. X    
  1746. X    crc = 0;
  1747. X    while (--len >= 0) {
  1748. X        /*
  1749. X         * Some fancy foot work here... The algorithm looks good
  1750. X         * in assembly, but in C it looks horrible!
  1751. X         */
  1752. X        crc = crc ^ (int) *buf++ << 8;
  1753. X        for (i=0; i<8; i++) {
  1754. X            if (crc & 0x8000)
  1755. X                crc = crc << 1 ^ 0x1021;
  1756. X            else
  1757. X                crc = crc << 1;
  1758. X        }
  1759. X    }
  1760. X    return(crc & 0xffff);
  1761. X}
  1762. X
  1763. X/*
  1764. X * Calculate the checksum for the given buffer.
  1765. X */
  1766. X
  1767. Xunsigned char
  1768. Xcalc_sum(buf, len)
  1769. Xunsigned char *buf;
  1770. Xint len;
  1771. X{
  1772. X    unsigned char sum;
  1773. X
  1774. X    sum = 0;
  1775. X    while (--len >= 0)
  1776. X        sum += *buf++;
  1777. X
  1778. X    return(sum);
  1779. X}
  1780. X
  1781. X/*
  1782. X * Get a character from the line with a specified time-out period in
  1783. X * seconds.  If the function times-out, it returns a -1.
  1784. X */
  1785. X
  1786. Xjmp_buf gl_jmp;
  1787. X
  1788. Xint
  1789. Xgetc_line(sec)
  1790. Xunsigned int sec;
  1791. X{
  1792. X    int force_gl();
  1793. X    unsigned char c;
  1794. X    unsigned int alarm();
  1795. X
  1796. X    signal(SIGALRM, force_gl);
  1797. X    if (setjmp(gl_jmp))
  1798. X        return(-1);
  1799. X
  1800. X    alarm(sec);
  1801. X    if (read(status->fd, (char *) &c, 1) <= 0) {
  1802. X        alarm(0);
  1803. X        return(-1);
  1804. X    }
  1805. X    alarm(0);
  1806. X    return(c);
  1807. X}
  1808. Xint
  1809. Xforce_gl(dummy)
  1810. Xint dummy;
  1811. X{
  1812. X    void longjmp();
  1813. X
  1814. X    longjmp(gl_jmp, 1);
  1815. X}
  1816. X
  1817. X/*
  1818. X * Same as above, but reads a bunch of characters.  The return code is
  1819. X * now just a success/fail indicator.
  1820. X */
  1821. X
  1822. Xjmp_buf rl_jmp;
  1823. X
  1824. Xint
  1825. Xfread_line(buf, len, sec)
  1826. Xunsigned char *buf;
  1827. Xunsigned int len, sec;
  1828. X{
  1829. X    int i, force_rl();
  1830. X    unsigned int alarm();
  1831. X
  1832. X    signal(SIGALRM, force_rl);
  1833. X    if (setjmp(rl_jmp))
  1834. X        return(-1);
  1835. X
  1836. X    alarm(sec);
  1837. X    /*
  1838. X     * Later, this will have some fine tuning to use more than
  1839. X     * single character I/O.
  1840. X     */
  1841. X    for (i=0; i<len; i++) {
  1842. X        if (read(status->fd, (char *) buf++, 1) <= 0) {
  1843. X            alarm(0);
  1844. X            return(-1);
  1845. X        }
  1846. X    }
  1847. X    alarm(0);
  1848. X    return(0);
  1849. X}
  1850. Xint
  1851. Xforce_rl(dummy)
  1852. Xint dummy;
  1853. X{
  1854. X    void longjmp();
  1855. X
  1856. X    longjmp(rl_jmp, 1);
  1857. X}
  1858. X
  1859. X/*
  1860. X * Put a character on the tty line.  This serves no useful purpose other
  1861. X * than making the code look pretty.
  1862. X */
  1863. X
  1864. Xint
  1865. Xputc_line(c)
  1866. Xunsigned char c;
  1867. X{
  1868. X    return(write(status->fd, (char *) &c, 1));
  1869. X}
  1870. X
  1871. X/*
  1872. X * Put the tty driver in the mode suitable for xmodem transfers.
  1873. X */
  1874. X
  1875. Xvoid
  1876. Xxmodem_mode(fd)
  1877. Xint fd;
  1878. X{
  1879. X    struct termio tbuf;
  1880. X
  1881. X    ioctl(fd, TCGETA, &tbuf);
  1882. X    /*
  1883. X     * Turn off the XON/XOFF flow control, turn off echoing, and
  1884. X     * switch to 8 bit no parity.  Later, the VTIME and VMIN variables
  1885. X     * will be fine tuned, to allow for more efficient I/O.
  1886. X     */
  1887. X    tbuf.c_cc[4] = 1;        /* VMIN */
  1888. X    tbuf.c_cc[5] = 0;        /* VTIME */
  1889. X    tbuf.c_iflag = 0;        /* no flow control or mapping */
  1890. X    tbuf.c_oflag = 0;        /* no char mapping or delays */
  1891. X    tbuf.c_lflag = 0;        /* no echo or signals */
  1892. X    tbuf.c_cflag &= ~PARENB;    /* no parity */
  1893. X    tbuf.c_cflag &= ~CSIZE;
  1894. X    tbuf.c_cflag |= CS8;        /* 8 bit */
  1895. X
  1896. X    ioctl(fd, TCSETA, &tbuf);
  1897. X    ioctl(fd, TCFLSH, 2);
  1898. X    return;
  1899. X}
  1900. X
  1901. X/*
  1902. X * Cancel the file transfer.  Send several ^X's to the remote, followed
  1903. X * by an equal number of backspace (in case they have already aborted and
  1904. X * we're really at the command line).
  1905. X */
  1906. X
  1907. Xvoid
  1908. Xcancel_xfer()
  1909. X{
  1910. X    extern char file_name[15];
  1911. X
  1912. X    if (!strcmp(param->abort, "DELETE"))
  1913. X        unlink(file_name);
  1914. X
  1915. X    putc_line(CAN);
  1916. X    putc_line(CAN);
  1917. X    putc_line(CAN);
  1918. X    putc_line(8);
  1919. X    putc_line(8);
  1920. X    putc_line(8);
  1921. X    return;
  1922. X}
  1923. X
  1924. X/*
  1925. X * Shorten a file to a predetermined length.  Used to remove the ^Z
  1926. X * padding from the end of files.  (Heaven help us, if one day a binary
  1927. X * file actually has ^Z's as part of the end of the file).
  1928. X */
  1929. X
  1930. Xint
  1931. Xfix_length(file, len)
  1932. Xchar *file;
  1933. Xint len;
  1934. X{
  1935. X    FILE *fp, *tempfp;
  1936. X    int num;
  1937. X    char *tempfile, *mktemp();
  1938. X    unsigned char buf[BUFSIZ];
  1939. X    struct stat stbuf;
  1940. X
  1941. X    if (stat(file, &stbuf) < 0)
  1942. X        return(1);
  1943. X                    /* see if we have any work to do */
  1944. X    if (len >= stbuf.st_size)
  1945. X        return(0);
  1946. X
  1947. X    if (!(fp = fopen(file, "r")))
  1948. X        return(1);
  1949. X
  1950. X    /*
  1951. X     * The temporary file should be in the same directory as the
  1952. X     * file being received because otherwise we'd have no way of
  1953. X     * guaranteeing they would be in the same file system.  (Hard
  1954. X     * links across different file systems aren't allowed).
  1955. X     */
  1956. X    tempfile = mktemp("trunXXXXXX");
  1957. X    if (!(tempfp = fopen(tempfile, "w"))) {
  1958. X        fclose(fp);
  1959. X        return(1);
  1960. X    }
  1961. X
  1962. X    while(len) {
  1963. X        num = (len > BUFSIZ) ? BUFSIZ : len;
  1964. X        fread((char *) buf, sizeof(buf[0]), num, fp);
  1965. X        fwrite((char *) buf, sizeof(buf[0]), num, tempfp);
  1966. X        len -= num;
  1967. X    }
  1968. X
  1969. X    fclose(fp);
  1970. X    fclose(tempfp);
  1971. X
  1972. X    if (unlink(file) < 0)
  1973. X        return(1);
  1974. X
  1975. X    if (link(tempfile, file) < 0)
  1976. X        return(1);
  1977. X
  1978. X    if (unlink(tempfile) < 0)
  1979. X        return(1);
  1980. X
  1981. X    return(0);
  1982. X}
  1983. SHAR_EOF
  1984. if test 4611 -ne "`wc -c < 'xmodem.c'`"
  1985. then
  1986.     echo shar: "error transmitting 'xmodem.c'" '(should have been 4611 characters)'
  1987. fi
  1988. fi
  1989. exit 0
  1990. #    End of shell archive
  1991.  
  1992.  
  1993.